//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Linq;
using JetBrains.Annotations;
using LargoCommon.Abstract;
using LargoCommon.Localization;
namespace LargoCommon.Music
{
///
/// Musical Block Model.
///
[Serializable]
public sealed class MelodicModel : AbstractModel, ICloneable
{
#region Fields
///
/// The melodic motives
///
private IEnumerable melodicMotives;
///
/// The used melodic motives
///
private Dictionary usedMelodicMotives;
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
public MelodicModel() {
this.MelodicMotives = new List();
}
#endregion
#region Properties
///
/// Gets or sets the harmonic order.
///
///
/// The harmonic order.
///
public byte HarmonicOrder { get; set; }
///
/// Gets or sets the melodic motives.
///
///
/// The melodic motives.
///
public IEnumerable MelodicMotives {
get {
Contract.Ensures(Contract.Result>() != null);
if (this.melodicMotives == null) {
throw new InvalidOperationException("Melodic motives are null.");
}
return this.melodicMotives;
}
set => this.melodicMotives = value ?? throw new ArgumentException(LocalizedMusic.String("Argument cannot be empty."), nameof(value));
}
///
/// Gets the first melodic bar.
///
/// Property description.
[UsedImplicitly]
public int FirstMelodicBar {
get {
var barNumber = (from c in this.BlockChanges.Changes where c.IsMelodicalNature orderby c.BarNumber select c.BarNumber).FirstOrDefault();
return barNumber;
}
}
#endregion
#region Static factory methods
///
/// Extracts the musical block model.
///
/// The musical block.
///
/// Returns value.
///
[UsedImplicitly]
public static MelodicModel GetNewModel(MusicalBlock musicalBlock) {
musicalBlock.Header.NumberOfLines = (byte)musicalBlock.Strip.Lines.Count;
var model = GetNewModel(musicalBlock.Header.Name, musicalBlock);
model.Number = musicalBlock.Header.Number;
model.SourceMusicalBlock = musicalBlock;
model.Header = musicalBlock.Header;
ProcessLogger.Singleton.SendLogEvent(null, LocalizedMusic.String("Analyzing musical lines..."), 0);
return model;
}
///
/// Gets the new musical model.
///
/// Name of the model.
/// The musical block.
///
/// Returns value.
///
/// Null Exception.
public static MelodicModel GetNewModel(string modelName, MusicalBlock musicalBlock) {
Contract.Requires(musicalBlock != null);
var model = new MelodicModel {
Name = modelName,
IsSelected = false
};
if (model == null) {
throw new ArgumentNullException(nameof(modelName));
}
model.Header = musicalBlock.Header;
model.SourceMusicalBlock = musicalBlock;
return model;
}
#endregion
#region Public methods
///
/// Gets the melodic motive.
///
/// The number.
/// Returns value.
[UsedImplicitly]
public MelodicMotive GetMelodicMotive(int number) {
var cnt = this.MelodicMotives.Count();
if (cnt == 0) {
return null;
}
var localNumber = number;
if (localNumber > cnt) {
checked {
localNumber = ((number - 1) % cnt) + 1;
}
}
//// MelodicMotive motive = this.MelodicMotives.ElementAt(localNumber);
var motive = (from m in this.MelodicMotives where m.Number == localNumber select m).FirstOrDefault();
return motive;
}
///
/// Adds the motive.
///
/// The motive.
public void AddMotive(MelodicMotive motive) {
Contract.Requires(motive != null);
//// motive.Core = this;
((List)this.MelodicMotives).Add(motive);
}
#endregion
///
/// Appends the melodic motives.
/// Main algorithm to determine motivic classes and their instances
///
/// The item groups.
public void AppendMelodicMotives(List itemGroups) {
if (this.MelodicMotives == null) {
return;
}
var lastMelodicIdentifier = string.Empty;
//// All motivic items ordered by length (descending) and identifier
var melodicItemGroups = (from ig in itemGroups orderby ig.Length descending, ig.MelodicIdentifier select ig).ToList();
MelodicMotive melodicMotive = null;
//// var numberWithinLength = 0; var lastLength = 0;
foreach (var itemGroup in melodicItemGroups) {
if (itemGroup.MusicalLine.FirstStatus.LineType != MusicalLineType.Melodic) {
continue;
}
//// if (itemGroup.Length != lastLength) { numberWithinLength = 0; lastLength = itemGroup.Length; }
var ident = itemGroup.MelodicIdentifier;
if (ident != lastMelodicIdentifier) { //// Step to next new motive
melodicMotive = itemGroup.MelodicMotive(0, string.Empty);
this.AddMotive(melodicMotive);
lastMelodicIdentifier = ident;
}
if (melodicMotive != null) {
melodicMotive.Occurrence++;
var area = itemGroup.GetArea();
this.SourceMusicalBlock.Body.MarkMelodicMotive(melodicMotive, area);
}
}
this.CompleteMotives();
}
///
/// Completes the motives.
///
public void CompleteMotives() {
var melodicMotiveNumber = 0;
var orderedMotives = (from m in this.MelodicMotives orderby m.Occurrence descending, m.Length descending select m).ToList();
foreach (var motive in orderedMotives) {
melodicMotiveNumber++;
//// string motiveName = string.Format(CultureInfo.InvariantCulture,"T{0}/R{1}", ("0" + musicalTrack.LineIndex.ToString(CultureInfo.CurrentCulture)).Right(2), ("0000" + this.MelodicAnalyzer.RhythmicMotiveNumber.ToString(CultureInfo.CurrentCulture)).Right(4));
//// var motiveName = string.Format(CultureInfo.InvariantCulture, "R{0}", ("0000" + rhythmicMotiveNumber.ToString(CultureInfo.CurrentCulture)).Right(4));
var motiveName = MusicalProperties.GetMotiveName(string.Empty, melodicMotiveNumber, motive.Length); //// rhythmicMotiveNumber
motive.Number = melodicMotiveNumber;
motive.Name = motiveName;
}
}
#region Unique motives
///
/// Get UniqueTMelodicMotive.
///
/// Unique Identifier.
/// Returns value.
[UsedImplicitly]
public MelodicMotive GetUniqueMelodicMotive(string uniqueIdentifier) {
if (this.usedMelodicMotives == null) {
this.usedMelodicMotives = new Dictionary();
}
var tmm = this.usedMelodicMotives.ContainsKey(uniqueIdentifier) ? this.usedMelodicMotives[uniqueIdentifier] : null;
if (tmm != null) {
return tmm;
}
//// Lock needed here
var melodicMotiveList = this.MelodicMotives;
foreach (var melodicMotive in
melodicMotiveList.Where(melodicMotive => melodicMotive != null
&& string.Compare(melodicMotive.UniqueIdentifier, uniqueIdentifier, StringComparison.Ordinal) == 0)) {
this.usedMelodicMotives[uniqueIdentifier] = melodicMotive;
return melodicMotive; //// Avoid multiple or conditional return statements.
}
return null;
}
#endregion
/// Makes a deep copy of the BlockModel object.
/// Returns object.
public object Clone() {
var model = new MelodicModel {
Name = this.Name,
Number = this.Number,
//// Core = this.Core,
Header = (MusicalHeader)this.Header.Clone(),
SourceMusicalBlock = this.SourceMusicalBlock
};
return model;
}
#region Material Extractor
///
/// Extract melodic material.
///
///
/// Returns value.
///
public MelodicMaterial ExtractMelodicMaterial() {
//// var dcm = DataBridgeMaterial.GetMaterialContext;
var tmm = new MelodicMaterial { Name = this.Name }; //// , CoreId = melodicCore.TMelodicCore.Id
//// dcm.AddToTMelodicMaterial(tmm);
var list = new Collection();
foreach (var structure in from motive in this.MelodicMotives
from structure in motive.MelodicStructures
let sc = structure.GetStructuralCode
where !string.IsNullOrEmpty(sc)
select structure) {
list.Add(structure);
}
var groupList = (from ms in list
group ms by ms.GetStructuralCode into g
select g).ToList();
groupList.ForEach(g => {
var s = g.FirstOrDefault();
if (s == null) {
return;
}
var ms = s; //// Clone?
ms.Occurrence = g.Count();
tmm.Structures.Add(ms);
});
//// dcm.SaveChanges();
return tmm;
}
#endregion
}
}